package com.baturu.vin.byd.service.impl;

import com.baturu.vin.base.exception.EPCDataNotStandardException;
import com.baturu.vin.base.service.impl.EPCAnalyser;
import com.baturu.vin.byd.dal.dao.*;
import com.baturu.vin.byd.dto.*;
import com.baturu.vin.dto.*;
import com.baturu.vin.kit.StreamKit;
import com.baturu.vin.service.EPCVinCacheService;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service("bydEPCAnalyser")
public class BydEPCAnalyser extends EPCAnalyser<BydVinInfoDTO,BydPartsMappingDTO,BydPartsInfoDTO,EPCCarTypeDTO> {

    @Autowired
    private BydVinInfoDAO bydVinInfoDAO;
    @Autowired
    private EPCVinCacheService epcVinCacheService;
    @Autowired
    private BydPartsMappingDAO partsMappingDAO;
    @Autowired
    private BydPartsInfoDAO bydPartsInfoDAO;
    @Autowired
    private BydImageDAO bydImageDAO;
    @Autowired
    private BydQueryResultDAO bydQueryResultDAO;

    @Override
    public BydVinInfoDTO analyseVinCode(String vinCode) {
        BydVinInfoDTO bydVinInfoDTO = bydVinInfoDAO.doFindVinInfo(vinCode);
        if(bydVinInfoDTO == null) {
            epcVinCacheService.addWaitCacheVinCode(EPCAnalyserEnum.BYD, vinCode);
        } else {
            //官网显示无法vin解析的,通过车系年款车型做解析(影响的是唯一率)
//            if(isIntegerNullOrZero(bydVinInfoDTO.getCarSystemId()) || isIntegerNullOrZero(bydVinInfoDTO.getCarTypeId()) || isIntegerNullOrZero(bydVinInfoDTO.getMjId())){
//                log.warn("解析到的vin:{}的信息无法生成车型",vinCode);
//                return null;
//            }
            BydVinInfoDTO cartypeInfo = bydVinInfoDAO.getCarSystemCarTypeMjId(bydVinInfoDTO);
            if(cartypeInfo == null) {
                log.warn("比亚迪数据问题", new EPCDataNotStandardException(String.format("无法解析vin:{%s},需要确定是否有新增车型信息", vinCode)));
                return null;
            }
            bydVinInfoDTO.setCarSystemId(cartypeInfo.getCarSystemId());
            bydVinInfoDTO.setCarTypeId(cartypeInfo.getCarTypeId());
            bydVinInfoDTO.setMjId(cartypeInfo.getMjId());
            Integer carTypeMjId = bydVinInfoDAO.getCarTypeMjId(bydVinInfoDTO);
            bydVinInfoDTO.setCarTypeMjId(carTypeMjId);
        }
        return bydVinInfoDTO;
    }

    @Override
    public BydVinInfoDTO analyseVinCodeByCache(String vinCode) {
        BydVinInfoDTO bydVinInfoDTO = bydVinInfoDAO.doFindVinInfo(vinCode);
        if(bydVinInfoDTO == null) {
            return null;
        }
        BydVinInfoDTO cartypeInfo = bydVinInfoDAO.getCarSystemCarTypeMjId(bydVinInfoDTO);
        if(cartypeInfo == null) {
            log.warn("比亚迪数据问题", new EPCDataNotStandardException(String.format("无法解析vin:{%s},需要确定是否有新增车型信息", vinCode)));
            return null;
        }
        bydVinInfoDTO.setCarSystemId(cartypeInfo.getCarSystemId());
        bydVinInfoDTO.setCarTypeId(cartypeInfo.getCarTypeId());
        bydVinInfoDTO.setMjId(cartypeInfo.getMjId());
        Integer carTypeMjId = bydVinInfoDAO.getCarTypeMjId(bydVinInfoDTO);
        bydVinInfoDTO.setCarTypeMjId(carTypeMjId);

        return bydVinInfoDTO;
    }

    private boolean isIntegerNullOrZero(Integer integer) {
        return integer == null || integer == 0;
    }

    @Override
    public List<EPCCarTypeDTO> queryCarTypes(BydVinInfoDTO analyseResult) {
        if(analyseResult == null) {
            return Collections.emptyList();
        }
        if (StringUtils.isBlank(analyseResult.getBtrId())) {
            return getBtrId(analyseResult);
        }
        EPCCarTypeDTO epcCarTypeDTO = new EPCCarTypeDTO();
        epcCarTypeDTO.setVinCode(analyseResult.getVinCode());
        epcCarTypeDTO.setEngineCode(analyseResult.getEngine());
        epcCarTypeDTO.setTransmissionCode(analyseResult.getGearBox());
        epcCarTypeDTO.setBtrId(analyseResult.getBtrId());
        return Lists.newArrayList(epcCarTypeDTO);
    }

    private List<EPCCarTypeDTO> getBtrId(BydVinInfoDTO analyseResult) {
        if(StringUtils.isBlank(analyseResult.getCarSystem())) {
            log.info("车系为空,无法获取BTRID");
            return Collections.emptyList();
        }
        if(StringUtils.isBlank(analyseResult.getCarType())) {
            log.info("车型为空,无法获取BTRID");
            return Collections.emptyList();
        }
        if(analyseResult.getYear() == null || analyseResult.getYear() == 0) {
            log.info("年款为空,无法获取BTRID");
            return Collections.emptyList();
        }
        String btrId = bydVinInfoDAO.getBtrId(analyseResult.getCarSystem(), analyseResult.getCarType(), analyseResult.getYear());
        if(StringUtils.isBlank(btrId)) {
            btrId = autoFillBtr(analyseResult.getCarSystem(), analyseResult.getCarType(), analyseResult.getYear(), analyseResult.getBrandName());
        }
        analyseResult.setBtrId(btrId);
        if(StringUtils.isBlank(analyseResult.getBtrId())) {
            return Collections.emptyList();
        }
        EPCCarTypeDTO epcCarTypeDTO = new EPCCarTypeDTO();
        epcCarTypeDTO.setVinCode(analyseResult.getVinCode());
        epcCarTypeDTO.setEngineCode(analyseResult.getEngine());
        epcCarTypeDTO.setTransmissionCode(analyseResult.getGearBox());
        epcCarTypeDTO.setBtrId(analyseResult.getBtrId());
        return Lists.newArrayList(epcCarTypeDTO);
    }

    private String autoFillBtr(String carSystem, String carType, Integer year, String brand) {
        if(StringUtils.isBlank(brand)) {
            brand = "比亚迪";
        }
        Integer carSystemId = bydQueryResultDAO.findCarSystemId(carSystem, brand);
        if(carSystemId==null || carSystemId == 0) {
            log.info("该车系不存在,请联系更新车系表:{}",carSystem);
            return null;
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String btrId = "BYDEPC" + sdf.format(new Date());
        String salesName = StringUtils.substring(carType, StringUtils.indexOf(carType,"(")+1, StringUtils.indexOf(carType,")"));
        String vCode = StringUtils.substring(carType,0, StringUtils.indexOf(carType,"("));
        String displacement = StringUtils.substring(salesName, 0, 4);
        if(!StringUtils.isNumeric(displacement.substring(0, 1))) {
            displacement = "";
        } else if(displacement.endsWith("排")) {
            displacement = StringUtils.substring(displacement, 0, 3) + "L";
        }
        CarTypeBuilderDTO carTypeDTO = CarTypeBuilderDTO.builder()
                .btrBrand(brand)
                .btrSubBrand(brand)
                .btrId(btrId)
                .carSystemId(carSystemId)
                .carSystem(carSystem)
                .carType(carType)
                .salesName(salesName)
                .productTime(String.valueOf(year))
                .displacement(displacement)
                .vcode(vCode)
                .build();
        bydVinInfoDAO.createCarTypeBtr(carSystem, carType, year, btrId);
        epcVinCacheService.saveNewCarType(Lists.newArrayList(carTypeDTO));
        return btrId;
    }

    @Override
    public List<BydPartsMappingDTO> queryPartsMappings(Set<String> btrPartsNames, BydVinInfoDTO analyseResult) {
        if(CollectionUtils.isEmpty(btrPartsNames)) {
            return Collections.emptyList();
        }
        return StreamKit.partitionStream(btrPartsNames, SEGMENT_QUERY_SIZE).flatMap(names->
                partsMappingDAO.getByBtrPartsName(names).stream()
        ).collect(Collectors.toList());
    }

    @Override
    public List<BydPartsInfoDTO> queryPartsInfoByPartsMapping(List<BydPartsMappingDTO> partsMappings, BydVinInfoDTO analyseResult) {
        if(CollectionUtils.isEmpty(partsMappings)) {
            log.info("主名映射数据为空");
            return Collections.emptyList();
        }
        Set<String> partsCodes = partsMappings.stream().map(BydPartsMappingDTO::getPartsCode).collect(Collectors.toSet());
        return queryPartsInfoByPartsCode(partsCodes, analyseResult);
    }

    @Override
    public List<BydPartsInfoDTO> queryPartsInfoByPartsCode(Set<String> partsCodes) {
        if(CollectionUtils.isEmpty(partsCodes)) {
            return Collections.emptyList();
        }
        return StreamKit.partitionStream(partsCodes, SEGMENT_QUERY_SIZE).flatMap(codes ->
                bydPartsInfoDAO.getPartsInfoByPartsCodes(codes).stream()
        ).collect(Collectors.toList());
    }

    @Override
    public List<BydPartsInfoDTO> queryPartsInfoByPartsCode(Set<String> partsCodes, BydVinInfoDTO analyseResult) {
        if(analyseResult == null || CollectionUtils.isEmpty(partsCodes)) {
            return Collections.emptyList();
        }
        List<BydPartsInfoDTO> partsInfo;
        if(analyseResult.getCarTypeMjId() != null && analyseResult.getCarTypeMjId() > 0) {
            partsInfo = StreamKit.partitionStream(partsCodes, SEGMENT_QUERY_SIZE).flatMap(codes ->
                    bydPartsInfoDAO.getPartsInfoByCarTypeMjId(analyseResult.getCarTypeMjId(), codes).stream()
            ).collect(Collectors.toList());
        } else {
            partsInfo = StreamKit.partitionStream(partsCodes, SEGMENT_QUERY_SIZE).flatMap(codes ->
                    bydPartsInfoDAO.getPartsInfoByCarSystemId(analyseResult.getCarSystemId(), codes).stream()
            ).collect(Collectors.toList());
        }
        return partsInfo;
    }

    @Override
    public List<BydPartsInfoDTO> filterPartsInfoByVinAnalyseResult(List<BydPartsInfoDTO> partsInfo, BydVinInfoDTO analyseResult) {
        return partsInfo;
    }

    @Override
    protected List<BydPartsMappingDTO> queryPartsMappingByPartsInfoAndAnalyseResult(Map<String, List<BydPartsInfoDTO>> partsInfoGroup, BydVinInfoDTO analyseResult) {
        Set<String> partsCodes = partsInfoGroup.values().stream()
                .flatMap(l -> l.stream().map(BydPartsInfoDTO::getPartsCode))
                .collect(Collectors.toSet());
        return StreamKit.partitionStream(partsCodes, SEGMENT_QUERY_SIZE).flatMap(l ->
                partsMappingDAO.getByPartsCode(l).stream()
        ).collect(Collectors.toList());
    }

    @Override
    public Map<String, List<EPCPartsImageDTO>> queryPartsImage(Map<String, List<BydPartsInfoDTO>> partsInfoGroup, BydVinInfoDTO analyseResult) {
        return partsInfoGroup.keySet().stream().collect(Collectors.toMap(e -> e,
                p -> {
                    List<BydPartsInfoDTO> bydPartsInfoDTOS = partsInfoGroup.get(p);
                    if(CollectionUtils.isEmpty(bydPartsInfoDTOS)) {
                        return Collections.emptyList();
                    }
                    fillImage(bydPartsInfoDTOS);
                    return bydPartsInfoDTOS.stream().filter(z-> StringUtils.isNotBlank(z.getImageUrl())).map(z->{
                        EPCPartsImageDTO imgDTO = new EPCPartsImageDTO();
                        imgDTO.setEpcPartsImagePositionDTOs(z.getPosition());
                        imgDTO.setImageName(z.getImageName());
                        imgDTO.setImageCode(z.getImageName());
                        imgDTO.setImageUrl(z.getImageUrl());
                        return imgDTO;
                    }).distinct().collect(Collectors.toList());
                }));
    }

    @Override
    public Map<String, Set<String>> queryReplaceCodes(Set<String> partsCodes, BydVinInfoDTO analyseResult) {
        if (CollectionUtils.isEmpty(partsCodes)) {
           return Collections.emptyMap();
        }
        List<BydReplaceCodeDTO> bydReplaceCodeDTOS = bydPartsInfoDAO.queryReplaceCode(partsCodes);
        if(CollectionUtils.isEmpty(bydReplaceCodeDTOS)) {
            return Collections.emptyMap();
        }
        Map<String, List<BydReplaceCodeDTO>> repMap = bydReplaceCodeDTOS.stream().collect(Collectors.groupingBy(BydReplaceCodeDTO::getPartsCode));
        return partsCodes.stream().collect(Collectors.toMap(o -> o, partsCode -> Optional.ofNullable(repMap.get(partsCode))
                .map(partsCodeList -> partsCodeList.stream().map(BydReplaceCodeDTO::getReplacePartsCode).collect(Collectors.toSet()))
                .orElseGet(Collections::emptySet)));
    }

    private void fillImage(List<BydPartsInfoDTO> partsInfo) {
        if(CollectionUtils.isEmpty(partsInfo)) {
            return;
        }
        Set<Long> legendIds = partsInfo.stream().map(BydPartsInfoDTO::getDataLegendId).collect(Collectors.toSet());
        List<BydImageDTO> bydImageDTOS = bydImageDAO.queryImageInfo(legendIds);
        if(CollectionUtils.isEmpty(bydImageDTOS)) {
            log.info("没有图片信息");
            return;
        }
        List<BydImageDTO> bydImagePositionDTOs = bydImageDAO.queryImagePosition(partsInfo);
        if (CollectionUtils.isEmpty(bydImagePositionDTOs)) {
            log.info("没有热点信息");
            return;
        }

        partsInfo.forEach(p->{
            if(p.getDataLegendId() == null || p.getDataLegendId()<0) {
                return;
            }
            BydImageDTO bydImageDTO = bydImageDTOS.stream().filter(z -> p.getDataLegendId().equals(z.getDataLegendId())).findFirst().orElseGet(null);
            if(bydImageDTO == null) {
                return;
            }
            Set<BydImageDTO> imagePositionDTOs = bydImagePositionDTOs.stream().filter(z -> p.getCallout().equalsIgnoreCase(z.getCallout()) && p.getDataLegendId().equals(z.getDataLegendId()) && p.getCarSystemId().equals(z.getCarSystemId()) && p.getDataGroupId().equals(z.getDataGroupId())).collect(Collectors.toSet());
            if(CollectionUtils.isEmpty(imagePositionDTOs)) {
                return;
            }
            p.setImageName(bydImageDTO.getImageName());
            String imageUrl = "legend/" + bydImageDTO.getImageName();
            String imgPath = appConfigLoader.getEPCPartsImageFullUrl(EPCAnalyserEnum.BYD, imageUrl);
            p.setImageUrl(imgPath);
            List<EPCPartsImagePositionDTO> epcPartsImagePositionDTOS = imagePositionDTOs.stream().map(e -> {
                EPCPartsImagePositionDTO epip = new EPCPartsImagePositionDTO();
                epip.setTopLeft(e.getTopLeft());
                epip.setBottomRight(e.getBottomRight());
                epip.setPosition(e.getCallout());
                return epip;
            }).distinct().collect(Collectors.toList());
            p.setPosition(epcPartsImagePositionDTOS);
        });
    }
}
